/*******************************************************************
 *
 * Copyright (C), 2021-2022, LS, All rights reserved.
 *
 * Author      : 
 * Create Time : 2020-11-02
 * Version     : v0.1
 * Description : 
 *******************************************************************/
 
/*----------------------------Head file----------------------------*/
#include "wm.h"
#include "string.h"
#include "osal_app.h"
#include "sys_task.h"
#include "hal_hall.h"
#include "app_time.h"
#include "app_pwr_mgr.h"
#include "app_skip_alg.h"
#include "app_store_mem.h"
#include "app_global_data.h"
#include "bsp_hw_config.h"
#include "user_config.h"

#define APP_SKIP_ALG_LOG_EN     0

#if APP_SKIP_ALG_LOG_EN
#include "log.h"
#define APP_ALG_LOG             LOG
#else
#define APP_ALG_LOG(...)
#endif

/*----------------------------macro file---------------------------*/
#define CONTI_SKIP_MAX_INV_MS       1300
#define FILTER_FIRST_SKIP_CNT_NUM   4

#define SYS_CURRENT_UTC_GET()       app_time_universal_utc_get()

#if(WORK_IN_SLEEP_MODE)
#define SYS_SLEEP_EXIT()            app_pwr_mgr_exit_sleep(PWR_MGR_MASK_SKIP_ALG)
#define SYS_SLEEP_ENTER()           app_pwr_mgr_enter_sleep(PWR_MGR_MASK_SKIP_ALG)
#else
#define SYS_SLEEP_EXIT()
#define SYS_SLEEP_ENTER()
#endif

#define SKIP_MAX_INV_TIMER_START()  osal_start_timerEx(osalApp_TaskID, APP_SKIP_MAX_INV_TIMER_EVT, CONTI_SKIP_MAX_INV_MS)

/*----------------------------type define--------------------------*/
typedef enum {
    HALL_NONE = 0,
    HALL_1,
    HALL_2,
    HALL_3
} hall_num_t;


#if(HALL3_PIN_ENABLE)
typedef enum {
    ROTATE_NONE = 0,
    ROTATE_FORWARD,
    ROTATE_REVERSE,
} hall_rotate_t;

typedef struct {
    uint8_t rotation;
    uint8_t hall_history[3];
} hall_record_t;

#else

typedef struct {
    uint8_t first_hall;
    uint8_t hall_history[2];
} hall_record_t;

#endif  //#if(HALL3_PIN_ENABLE)

/*----------------------------var define---------------------------*/
static hall_record_t hall_record;
static uint16_t random_tmp_cnt = 0;
static bool detect_trip_skip = false;
static bool filter_first_skip_in_group = false;
static uint16_t cur_consecutive_skip_sec = 0;
static uint16_t cur_consecutive_skip_num = 0;

#if(LEIJISHU)
static uint8_t filter_first_skip_cnt = 0;

extern uint32_t clr_skip_data_cnt;
extern bool trip_valid;
#endif

/*-------------------------func declaration------------------------*/

/*-----------------------------------------------------------------*/

void app_skip_btn_unlock(void)
{
    g_app_data.sys.state.btn_lock = false;
}


static void clear_consecutive_skip_param(void)
{
    cur_consecutive_skip_sec = 0;
    cur_consecutive_skip_num = 0;
}

void compare_and_set_consecutive_skip_param(void)
{
    skip_packet_t *g_skip = &g_app_data.tmp.skip;

    if(g_skip->consecutive_skip_max_num < cur_consecutive_skip_num) {
        g_skip->consecutive_skip_max_num = cur_consecutive_skip_num;
    }

    uint16_t temp_sec = cur_consecutive_skip_sec;
    if(temp_sec <= 0) {
        temp_sec = 1;
    }
    uint16_t freq = (cur_consecutive_skip_num*60)/temp_sec;
    if(g_skip->freq_max < freq) {
        g_skip->freq_max = freq;
    }
}


static void clear_hall_det_cnt(void)
{
    memset((uint8_t*)&hall_record, 0, sizeof(hall_record));
}


static void trip_skip_detect_handle(void)
{
    APP_ALG_LOG("detect_trip_skip.\n");

    skip_packet_t *g_skip = &g_app_data.tmp.skip;

    if(g_skip->trip_num < (SKIP_GROUP_MAX_NUM - 1)) {
        if(g_skip->group[g_skip->trip_num].skip_sec <= 0) {		//Can not be zero secs, App need more than 1 sec.
            g_skip->group[g_skip->trip_num].skip_sec = 1;
        }
        g_skip->trip_num++;
    }
}


void app_skip_result_calc_and_save(void)
{
    skip_packet_t *g_skip = &g_app_data.tmp.skip;

    if(g_skip->group[g_skip->trip_num].skip_sec <= 0) {	//Can not be zero secs, App need more than 1 sec.
        g_skip->group[g_skip->trip_num].skip_sec = 1;
    }

    g_skip->freq_avg = (g_skip->total_cnts*60)/g_skip->total_secs;
    compare_and_set_consecutive_skip_param();

    app_mem_skip_data_save();
}


void app_skip_sec_update_handle(void)
{
    if(g_app_data.sys.state.skip_done)
        return;

    skip_packet_t *g_skip = &g_app_data.tmp.skip;
    uint16_t countdown_time = g_app_data.user.countdown_time;

    if(SKIP_MODE_FREE == g_skip->mode) {
        //User does not jump any more.
        if(random_tmp_cnt == g_skip->total_cnts)
            return;

        if(g_skip->total_secs < DISP_SKIP_SEC_MAX_NUM) {
            g_skip->total_secs++;
            g_skip->group[g_skip->trip_num].skip_sec++;
            g_skip->skip_valid_sec++;
            cur_consecutive_skip_sec++;
            random_tmp_cnt = g_skip->total_cnts;
        }
    }
    else if(SKIP_MODE_COUNTDOWN_TIME == g_skip->mode) {
        if(g_skip->total_secs < countdown_time) {
            if(g_skip->total_cnts > 0) {
                g_skip->total_secs++;
                g_skip->group[g_skip->trip_num].skip_sec++;
                cur_consecutive_skip_sec++;
                if(true == g_app_data.sys.state.btn_lock) {
                    g_skip->skip_valid_sec++;
                }
            }
        }
        if(g_skip->total_secs >= countdown_time) {
            g_app_data.sys.state.skip_done = true;
            sys_task_msg_send(MSG_TYPE_UI, UI_EVT_DEV_SKIP_DONE);
        }
    }
#if(LEIJISHU == 0)
    else if(SKIP_MODE_COUNTDOWN_NUM == g_skip->mode) {
        if(g_skip->total_secs < DISP_SKIP_SEC_MAX_NUM && g_skip->total_cnts > 0) {
            g_skip->total_secs++;
            g_skip->group[g_skip->trip_num].skip_sec++;
            cur_consecutive_skip_sec++;
            if(true == g_app_data.sys.state.btn_lock) {
                g_skip->skip_valid_sec++;
            }
        }
    }
#else   //#if(LEIJISHU == 0)
    else if(SKIP_MODE_CUMULATIVE_NUM == g_skip->mode) {
        //User does not jump any more.
        if(random_tmp_cnt == g_skip->total_cnts)
            return;

        if(g_skip->total_secs < DISP_SKIP_SEC_MAX_NUM) {
            g_skip->total_secs++;
            g_skip->group[g_skip->trip_num].skip_sec++;
            g_skip->skip_valid_sec++;
            cur_consecutive_skip_sec++;
            random_tmp_cnt = g_skip->total_cnts;
        }
    }
#endif  //#if(LEIJISHU == 0)
}


static void app_skip_mode_count_handle(void)
{
    if(g_app_data.sys.state.skip_done)
        return;

    skip_packet_t *g_skip = &g_app_data.tmp.skip;
#if(LEIJISHU == 0)
    uint16_t countdown_num = g_app_data.user.countdown_num;
#endif

    if(SKIP_MODE_FREE == g_skip->mode) {
        if(g_skip->total_cnts < DISP_SKIP_CNT_MAX_NUM) {
            g_skip->total_cnts++;
            if(0 == g_skip->group[g_skip->trip_num].skip_cnt) {
                g_skip->total_secs++;
                g_skip->skip_valid_sec++;
                g_skip->group[g_skip->trip_num].skip_sec++;
                cur_consecutive_skip_sec++;
            }
            g_skip->group[g_skip->trip_num].skip_cnt++;
            cur_consecutive_skip_num++;
        }
    }
    else if(SKIP_MODE_COUNTDOWN_TIME == g_skip->mode) {
        if(g_skip->total_cnts < DISP_SKIP_CNT_MAX_NUM) {
            g_skip->total_cnts++;
            g_skip->group[g_skip->trip_num].skip_cnt++;
            cur_consecutive_skip_num++;
        }
    }
#if(LEIJISHU == 0)
    else if(SKIP_MODE_COUNTDOWN_NUM == g_skip->mode) {
        if(g_skip->total_cnts < countdown_num) {
            g_skip->total_cnts++;
            g_skip->group[g_skip->trip_num].skip_cnt++;
            cur_consecutive_skip_num++;
        }
        if(g_skip->total_cnts >= countdown_num) {
            g_app_data.sys.state.skip_done = true;
            sys_task_msg_send(MSG_TYPE_UI, UI_EVT_DEV_SKIP_DONE);
        }
    }
#else   //#if(LEIJISHU == 0)
    else if(SKIP_MODE_CUMULATIVE_NUM == g_skip->mode) {
        if(g_skip->total_cnts < ALG_SKIP_CNT_MAX_NUM) {
            g_skip->total_cnts++;
            if(0 == g_skip->group[g_skip->trip_num].skip_cnt) {
                g_skip->total_secs++;
                g_skip->skip_valid_sec++;
                g_skip->group[g_skip->trip_num].skip_sec++;
                cur_consecutive_skip_sec++;
            }
            g_skip->group[g_skip->trip_num].skip_cnt++;
            cur_consecutive_skip_num++;
        }
    }
#endif  //#if(LEIJISHU == 0)

    if(g_skip->start_utc == 0 && g_skip->total_cnts > 0) {
        g_skip->start_utc = SYS_CURRENT_UTC_GET();
    }

    APP_ALG_LOG("total_cnts: %d.\n", g_skip->total_cnts);
    APP_ALG_LOG("total_secs: %d.\n", g_skip->total_secs);
}


#if(HALL3_PIN_ENABLE)

void app_skip_algorithm_handle(uint8_t pin_no, bool active)
{
    if(g_app_data.sys.state.skip_done)
        return;

    //APP_ALG_LOG("pin_no: %d(%d).\n", pin_no, active);
    bool rotation_valid = false;
    bool skip_valid = false;
    uint8_t tmp_hall = HALL_NONE;

    if(true == active && HALL_1 == pin_no) {
        tmp_hall = HALL_1;
    }
    else if(true == active && HALL_2 == pin_no) {
        tmp_hall = HALL_2;
    }
    else if(true == active && HALL_3 == pin_no) {
        tmp_hall = HALL_3;
    }
    else {
        return;
    }

    if (hall_record.hall_history[0] != tmp_hall) {
        hall_record.hall_history[2] = hall_record.hall_history[1];
        hall_record.hall_history[1] = hall_record.hall_history[0];
        hall_record.hall_history[0] = tmp_hall;
        
        if(hall_record.hall_history[0] != HALL_NONE && hall_record.hall_history[1] != HALL_NONE && hall_record.hall_history[2] != HALL_NONE)
        {
            if (hall_record.hall_history[2] > hall_record.hall_history[1] && hall_record.hall_history[1] > hall_record.hall_history[0]) {
                if(ROTATE_REVERSE != hall_record.rotation) {
                    rotation_valid = true;
                }
                hall_record.rotation = ROTATE_FORWARD;
            }
            else if(hall_record.hall_history[0] > hall_record.hall_history[1] && hall_record.hall_history[1] > hall_record.hall_history[2]) {
                if(ROTATE_FORWARD != hall_record.rotation) {
                    rotation_valid = true;
                }
                hall_record.rotation = ROTATE_REVERSE;
            }
               
            if(true == rotation_valid)
            {
                wm_disp_off_cnt_reset();
                if(true == filter_first_skip_in_group) { //Filter the only one jump.
                    filter_first_skip_in_group = false;
                    SYS_SLEEP_EXIT();
                    SKIP_MAX_INV_TIMER_START();
                    return;
                }
                else {
#if 0//(LEIJISHU == 1)
                    if(g_app_data.tmp.skip.total_cnts <= 0 && filter_first_skip_cnt < FILTER_FIRST_SKIP_CNT_NUM) {
                        filter_first_skip_cnt++;
                        SKIP_MAX_INV_TIMER_START();
                        return;
                    }
                    else {
                        if(FILTER_FIRST_SKIP_CNT_NUM == filter_first_skip_cnt) {
                            g_app_data.tmp.skip.total_cnts += FILTER_FIRST_SKIP_CNT_NUM;
                            g_app_data.tmp.skip.group[g_app_data.tmp.skip.trip_num].skip_cnt += FILTER_FIRST_SKIP_CNT_NUM;
                            filter_first_skip_cnt++;
                        }
                        skip_valid = true;
                    }
#else
                    skip_valid = true;
#endif
                }
            }
        }
    }

    if(skip_valid) {
        if(detect_trip_skip) {
            detect_trip_skip = false;
            if(g_app_data.tmp.skip.total_cnts > 0) {
                trip_skip_detect_handle();
            }
        }
        app_disp_backlight_set(false); //Aneroid battery may lost power for a while, closing backlight can save more power to make capacitance supply MCU work more time.
        app_skip_mode_count_handle(); //We do not count the last jump, cause user already stop.
        g_app_data.sys.state.btn_lock = true;
        SYS_SLEEP_EXIT();
        SKIP_MAX_INV_TIMER_START();
    }
}

#else

void app_skip_algorithm_handle(uint8_t pin_no, bool active)
{
    //APP_ALG_LOG("pin: %d.\n", pin_no);
    if(g_app_data.sys.state.skip_done)
        return;

    bool skip_valid = false;
    uint8_t tmp_hall = HALL_NONE;

    if(true == active && HALL_1 == pin_no) {
        tmp_hall = HALL_1;
    }
    else if(true == active && HALL_2 == pin_no) {
        tmp_hall = HALL_2;
    }
    else {
        return;
    }

    if (hall_record.first_hall == HALL_NONE) {
        hall_record.first_hall = tmp_hall;
    }

    if (hall_record.hall_history[0] != tmp_hall) {
        hall_record.hall_history[1] = hall_record.hall_history[0];
        hall_record.hall_history[0] = tmp_hall;
        
        if (hall_record.hall_history[1] != hall_record.first_hall && HALL_NONE != hall_record.hall_history[1]) {
            //clear_hall_det_cnt();
            wm_disp_off_cnt_reset();
            if(true == filter_first_skip_in_group) { //Filter the only one jump.
                filter_first_skip_in_group = false;
                SYS_SLEEP_EXIT();
                SKIP_MAX_INV_TIMER_START();
                return;
            }
            else {
                if(SKIP_MODE_CUMULATIVE_NUM == g_app_data.tmp.skip.mode)
                {
                    if(g_app_data.tmp.skip.total_cnts <= 0 && filter_first_skip_cnt < FILTER_FIRST_SKIP_CNT_NUM) {
                        filter_first_skip_cnt++;
                        SKIP_MAX_INV_TIMER_START();
                        return;
                    }
                    else {
                        if(FILTER_FIRST_SKIP_CNT_NUM == filter_first_skip_cnt) {
                            g_app_data.tmp.skip.total_cnts += FILTER_FIRST_SKIP_CNT_NUM;
                            g_app_data.tmp.skip.group[g_app_data.tmp.skip.trip_num].skip_cnt += FILTER_FIRST_SKIP_CNT_NUM;
                            filter_first_skip_cnt++;
                        }
                        skip_valid = true;
                    }
                }
                else
                {
                    skip_valid = true;
                }
            }
        }
    }

    if(skip_valid) {
        if(detect_trip_skip) {
            detect_trip_skip = false;
            if(g_app_data.tmp.skip.total_cnts > 0) {
                trip_skip_detect_handle();
            }
        }
        app_disp_backlight_set(false); //Aneroid battery may lost power for a while, closing backlight can save more power to make capacitance supply MCU work more time.
        app_skip_mode_count_handle(); //We do not count the last jump, cause user already stop.
        g_app_data.sys.state.btn_lock = true;
        SYS_SLEEP_EXIT();
        SKIP_MAX_INV_TIMER_START();
    }
}
#endif      //#if(HALL3_PIN_ENABLE)


void app_skip_mode_set(uint8_t mode)
{
    if(mode != SKIP_MODE_NULL && mode >= SKIP_MODE_MAX)
        return;

    if(SKIP_MODE_NULL == mode) {
        app_skip_btn_unlock();
        hal_hall_hw_deinit();
        return;
    }
    else {
        hal_hall_hw_init(app_skip_algorithm_handle);
    }

#if(SCHOOL_ADV_ENABLE)

    #if(LEIJISHU == 0)
    random_tmp_cnt = 0;
    memset((uint8_t*)&g_app_data.tmp.skip, 0, sizeof(skip_packet_t));
    memset((uint8_t*)&g_app_data.tmp.trip_group, 0, sizeof(g_app_data.tmp.trip_group));
    memset((uint8_t*)&g_app_data.tmp.skip_group, 0, sizeof(g_app_data.tmp.skip_group));
    #else   //#if(LEIJISHU == 0)
    if(SKIP_MODE_CUMULATIVE_NUM == mode) {
        uint32_t utc = SYS_CURRENT_UTC_GET();
        if(utc >= clr_skip_data_cnt && (utc - clr_skip_data_cnt) > 3600) {
            random_tmp_cnt = 0;
            g_app_data.tmp.tmp_skip_record = 0;
            memset((uint8_t*)&g_app_data.tmp.skip, 0, sizeof(skip_packet_t));
        }
    }
    else {
        random_tmp_cnt = 0;
        g_app_data.tmp.tmp_skip_record = 0;
        memset((uint8_t*)&g_app_data.tmp.skip, 0, sizeof(skip_packet_t));
    }
    #endif  //#if(LEIJISHU == 0)
    
#else   //#if(SCHOOL_ADV_ENABLE)
    memset((uint8_t*)&g_app_data.tmp.skip, 0, sizeof(skip_packet_t));
#endif  //#if(SCHOOL_ADV_ENABLE)

    clear_hall_det_cnt();
    clear_consecutive_skip_param();
    random_tmp_cnt = 0;
    detect_trip_skip = false;
#if(LEIJISHU)
    trip_valid = false;
    filter_first_skip_cnt = 0;
#endif
    filter_first_skip_in_group = true;
    g_app_data.tmp.skip.mode = mode;
    if(SKIP_MODE_COUNTDOWN_TIME == mode) {
        g_app_data.tmp.skip.setting = g_app_data.user.countdown_time;
    }
    else if(SKIP_MODE_COUNTDOWN_NUM == mode) {
        g_app_data.tmp.skip.setting = g_app_data.user.countdown_num;
    }
    g_app_data.sys.state.skip_done = false;
    wm_disp_off_and_bl_cnt_reset();
}


void app_skip_alg_max_inv_timeout_handle(void)
{
    compare_and_set_consecutive_skip_param();
    clear_consecutive_skip_param();
    detect_trip_skip = true;
#if(LEIJISHU)
    trip_valid = true;
    filter_first_skip_cnt = 0;
#endif
    filter_first_skip_in_group = true;
    clear_hall_det_cnt();
    app_skip_btn_unlock();
    SYS_SLEEP_ENTER();
}


void app_skip_hw_init(void)
{
    hal_hall_hw_init(app_skip_algorithm_handle);
}


